package cn.net.dckj.common.pay.common;

import cn.net.dckj.common.pay.wx.api.WxPayApiConfig.SignType;
import cn.net.dckj.common.pay.wx.api.WxPayApiConfigKit;
import cn.net.dckj.common.utils.Encodes;
import cn.net.dckj.common.utils.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

/**
 * 微信支付的统一下单工具类
 */
public class PaymentKit {

    /**
     * 构建短链接参数
     *
     * @param appid
     * @param sub_appid
     * @param mch_id
     * @param sub_mch_id
     * @param long_url
     * @param paternerKey
     * @return <Map<String, String>>
     */
    public static Map<String, String> buildShortUrlParasMap(String appid, String sub_appid, String mch_id,
                                                            String sub_mch_id, String long_url, String paternerKey) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appid);
        params.put("sub_appid", sub_appid);
        params.put("mch_id", mch_id);
        params.put("sub_mch_id", sub_mch_id);
        params.put("long_url", long_url);

        return buildSignAfterParasMap(params, paternerKey);

    }

    /**
     * 组装签名的字段
     *
     * @param params     参数
     * @param urlEncoder 是否urlEncoder
     * @return {String}
     */
    public static String packageSign(Map<String, String> params, boolean urlEncoder) {
        // 先将参数以其参数名的字典序升序进行排序
        TreeMap<String, String> sortedParams = new TreeMap<String, String>(params);
        // 遍历排序后的字典，将所有参数按"key=value"格式拼接在一起
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Entry<String, String> param : sortedParams.entrySet()) {
            String value = param.getValue();
            if (StringUtils.isBlank(value)) {
                continue;
            }
            if (first) {
                first = false;
            } else {
                sb.append("&");
            }
            sb.append(param.getKey()).append("=");
            if (urlEncoder) {
                try {
                    value = urlEncode(value);
                } catch (UnsupportedEncodingException e) {
                }
            }
            sb.append(value);
        }
        return sb.toString();
    }

    /**
     * urlEncode
     *
     * @param src 微信参数
     * @return String
     * @throws UnsupportedEncodingException 编码错误
     */
    public static String urlEncode(String src) throws UnsupportedEncodingException {
        return Encodes.urlEncode(src).replace("+", "%20");
    }

    /**
     * 构建签名之后的参数
     *
     * @param params
     * @param paternerKey
     * @return <Map<String, String>>
     */
    public static Map<String, String> buildSignAfterParasMap(Map<String, String> params, String paternerKey) {
        params.put("nonce_str", String.valueOf(System.currentTimeMillis()));
        String sign = PaymentKit.createSign(params, paternerKey);
        params.put("sign", sign);
        return params;
    }

    /**
     * 生成签名
     *
     * @param params     参数
     * @param partnerKey 支付密钥
     * @return {String}
     */
    public static String createSign(Map<String, String> params, String partnerKey) {
        // 生成签名前先去除sign
        params.remove("sign");
        String stringA = packageSign(params, false);
        String stringSignTemp = stringA + "&key=" + partnerKey;
        return HashKit.md5(stringSignTemp).toUpperCase();
    }

    public static String createSign(Map<String, String> params, String partnerKey, SignType signType) throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException {
        // 生成签名前先去除sign
        params.remove("sign");
        String stringA = packageSign(params, false);
        String stringSignTemp = stringA + "&key=" + partnerKey;
        if (signType == SignType.MD5) {
            return HashKit.md5(stringSignTemp).toUpperCase();
        } else {
            return hmacSHA256(stringSignTemp, partnerKey).toUpperCase();
        }
    }

    /**
     * 解密退款结果通知中的req_info
     *
     * @param reqInfo
     * @param paternerKey
     * @return {String}
     * @throws Exception
     */
    public static String decryptRefundData(String reqInfo, String paternerKey) throws Exception {
        return AesKit.decryptData(reqInfo, paternerKey);
    }

    /**
     * 支付异步通知时校验sign
     *
     * @param params      参数
     * @param paternerKey 支付密钥
     * @return {boolean}
     */
    public static boolean verifyNotify(Map<String, String> params, String paternerKey) {
        String sign = params.get("sign");
        String localSign = PaymentKit.createSign(params, paternerKey);
        return sign.equals(localSign);
    }

    public static boolean verifyNotify(Map<String, String> params, String paternerKey, SignType signType) throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException {
        String sign = params.get("sign");
        String localSign = PaymentKit.createSign(params, paternerKey, signType);
        return sign.equals(localSign);
    }

    /**
     * 预付订单再次签名
     *
     * @param prepay_id
     * @return <Map<String, String>>
     */
    public static Map<String, String> prepayIdCreateSign(String prepay_id) {
        Map<String, String> packageParams = new HashMap<String, String>();
        packageParams.put("appId", WxPayApiConfigKit.getWxPayApiConfig().getAppId());
        packageParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
        packageParams.put("nonceStr", String.valueOf(System.currentTimeMillis()));
        packageParams.put("package", "prepay_id=" + prepay_id);
        packageParams.put("signType", "MD5");
        String packageSign = PaymentKit.createSign(packageParams,
                WxPayApiConfigKit.getWxPayApiConfig().getPaternerKey());
        packageParams.put("paySign", packageSign);
        return packageParams;
    }

    /**
     * 判断接口返回的code是否是SUCCESS
     *
     * @param return_code
     * @return {boolean}
     */
    public static boolean codeIsOK(String return_code) {
        return StringUtils.isNotBlank(return_code) && "SUCCESS".equals(return_code);
    }

    /**
     * 微信下单map to xml
     *
     * @param params 参数
     * @return {String}
     */
    public static String toXml(Map<String, String> params) {
        StringBuilder xml = new StringBuilder();
        xml.append("<xml>");
        for (Entry<String, String> entry : params.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            // 略过空值
            if (StringUtils.isBlank(value))
                continue;
            xml.append("<").append(key).append(">");
            xml.append(entry.getValue());
            xml.append("</").append(key).append(">");
        }
        xml.append("</xml>");
        return xml.toString();
    }

    /**
     * 针对支付的xml，没有嵌套节点的简单处理
     *
     * @param xmlStr xml字符串
     * @return <Map<String, String>>
     */
    public static Map<String, String> xmlToMap(String xmlStr) {
        XmlHelper xmlHelper = XmlHelper.of(xmlStr);
        return xmlHelper.toMap();
    }

    /**
     * 替换url中的参数
     *
     * @param str
     * @param regex
     * @param args
     * @return {String}
     */
    public static String replace(String str, String regex, String... args) {
        int length = args.length;
        for (int i = 0; i < length; i++) {
            str = str.replaceFirst(regex, args[i]);
        }
        return str;
    }

    /**
     * 将加密后的字节数组转换成字符串
     */
    public static String byteArrayToHexString(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b != null && n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1)
                hs.append('0');
            hs.append(stmp);
        }
        return hs.toString().toLowerCase();
    }

    /**
     * HmacSHA256 签名
     *
     * @param stringSignTemp
     * @param partnerKey
     * @return
     * @throws Exception
     */
    public static String hmacSHA256(String stringSignTemp, String partnerKey) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(partnerKey.getBytes(), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        return byteArrayToHexString(sha256_HMAC.doFinal(stringSignTemp.getBytes("utf-8")));
    }
}